package gmodel

import (
	"fmt"
	"gitee.com/851537/gbase"
	"github.com/pkg/errors"
	"reflect"
	"strings"
)

type ITree interface {
	IDb

	//impl by real struct
	GetPid() int64
	GetPath() string
	//NewNode(id ...int64) ITree

	DSonNum(tree ITree) int64

	IsRoot(child ITree) bool
}

type BaseTree struct {
	BaseDbRedisSet

	//Children []ITree `orm:"-" json:"children"`
}

func (this *BaseTree) IsRoot(child ITree) bool {
	return child.GetPid() == 0
}

func (this *BaseTree) DSonNum(child ITree) int64 {
	count, _ := this.DbCount(child, "", "pid", child.GetId())
	return count
}

func (this *BaseTree) MkSonPath(child ITree) string {
	if child.GetId() == 0 {
		return ","
	} else {
		return fmt.Sprintf("%s%d,", child.GetPath(), child.GetId())
	}
}

func (this *BaseTree) HasDSon(child ITree, dson ITree) bool {
	return child.GetId() == dson.GetPid()
}

//func (this *BaseTree) HasGSon(child ITree,gsonOrId interface{}) bool {
//	var (
//		id   int64
//		ok    bool
//		err   error
//	)
//	if id, ok = gsonOrId.(int64); ok {
//
//		admin = &Admin{Id: uid}
//
//		if err = admin.DbRedisGet(admin, false); err != nil {
//			return false
//		}
//		return this.Id == admin.Pid
//	} else if admin, ok = uidOrAdmin.(*Admin); ok {
//		return this.Id == admin.Pid
//	}
//	return false
//}

// depend this.id
func (this *BaseTree) HasSon(child ITree, son ITree) bool {
	if has := this.HasDSon(child, son); has {
		return true
	} else if strings.Index(son.GetPath(), fmt.Sprintf(",%d,", child.GetId())) != -1 {
		return true
	}
	return false
}

// get all son ids,contain dson and gson

func (this *BaseTree) GetSonIds(child ITree, includeSelf ...bool) (ids []int64, err error) {
	var (
		list []map[string]any
	)
	ids = make([]int64, 0)
	if len(includeSelf) > 0 && includeSelf[0] {
		ids = append(ids, child.GetId())
	}
	_, err = this.DbPage(child, &list, "t.id", "", "", 1, -1, "t.path like ?", fmt.Sprintf("%%,%d,%%", child.GetId()))
	if err != nil {
		return nil, err
	}
	for _, v := range list {
		//id, _ := ameda.StringToInt64(v["id"].(string))
		ids = append(ids, v["id"].(int64))
	}
	return ids, nil
}

func (this *BaseTree) GetDSonIds(child ITree) (ids []int64, err error) {
	var (
		list []map[string]any
	)
	ids = make([]int64, 0)
	_, err = this.DbPage(child, &list, "t.id", "", "", 1, -1, "t.pid", child.GetId())
	if err != nil {
		return nil, err
	}
	for _, v := range list {
		//id, _ := ameda.StringToInt64(v["id"].(string))
		ids = append(ids, v["id"].(int64))
	}
	return ids, nil
}

// fmt:,1,2,3, 包含自己的id
func (this *BaseTree) GetWholePath(child ITree) string {
	return fmt.Sprintf("%s%d,", child.GetPath(), child.GetId())
}

//func (this *BaseTree) GetParent(child ITree, parent ITree) (err error) {
//	return this.DbGet(parent, "t.id", child.GetPid())
//}

func (this *BaseTree) GetParent(child ITree) (parent ITree, err error) {
	//parent = child.NewNode(child.GetPid())
	parent = reflect.New(reflect.TypeOf(child).Elem()).Interface().(ITree)
	err = this.DbGet(parent, "t.id", child.GetPid())
	return
}

// 找到最近一个满足find判断方法的上级
func (this *BaseTree) GetParentAt(child ITree, check func(item ITree) bool) (parent ITree, err error) {
	//parent = child.NewNode(child.GetPid())
	pid := child.GetPid()
	for i := 0; i < 99999; i++ { //最多支持10万级
		parent = reflect.New(reflect.TypeOf(child).Elem()).Interface().(ITree)
		if err = this.DbGet(parent, "t.id", pid); err != nil {
			return nil, errors.New("未找到")
		} else if check(parent) {
			return parent, nil
		}
		pid = parent.GetPid()
	}
	return nil, errors.New("未找到")
}

func (this *BaseTree) GetParents(child ITree) (parents []ITree, err error) {
	cur := child
	for {
		if cur.IsRoot(cur) {
			break
		}
		var parent ITree
		if parent, err = this.GetParent(cur); err != nil {
			return
		}
		parents = append(parents, parent)
		cur = parent
	}
	return
}

func (this *BaseTree) GetRoot(child ITree, root ITree) error {
	path := gbase.Path[int64](child.GetPath())
	if path.Empty() {
		root = child
	} else {
		pathArr := path.MustToList()
		return this.DbGet(root, "t.id", pathArr[0])
	}
	return nil
}
